import json
import math
from uuid import uuid1

import ezdxf

from app.tools import Normal
from app.tools.utils import THRESHOLD, twist, ellipse, arc

def _twist(x, y, angle):
    a = math.pi * angle / 180
    _x = x * math.cos(a) - y * math.sin(a)
    _y = x * math.sin(a) + y * math.cos(a);
    return [_x, _y]


def _insert(p, i):
    d = [(p[0] - i['bx']) * i['xs'], (p[1] - i['by']) * i['ys']]
    d = _twist(d[0], d[1], i['r'])
    return [i['x'] + d[0], i['y'] + d[1]]


def _entity(e, tiny, meta_id, normal, i=None):
    item = {'id': str(uuid1()), 'meta_id': meta_id, 'type': 'line', 'points': []}

    if e.dxftype() == 'LWPOLYLINE':
        with e.points('xy') as points:
            for p in points:
                if i is not None:
                    p = _insert(p, i)
                item['points'].append([normal.nx(p[0]), normal.ny(p[1])])
        if e.closed:
            item['points'].append(item['points'][0])
    elif e.dxftype() == 'TEXT':
        item['type'] = 'text'
        item['text'], item['rotation'] = e.dxf.text, e.dxf.rotation
        item['height'], item['width'] = e.dxf.height, e.dxf.width
        x, y = e.dxf.insert.x, e.dxf.insert.y
        if i is not None:
            x, y = _insert([x, y], i)
        item['points'].append([normal.nx(x), normal.ny(y)])
    elif e.dxftype() == 'LINE':
        sx, sy = e.dxf.start.x, e.dxf.start.y
        if i is not None:
            sx, sy = _insert([sx, sy], i)
        item['points'].append([normal.nx(sx), normal.ny(sy)])
        ex, ey = e.dxf.end.x, e.dxf.end.y
        if i is not None:
            ex, ey = _insert([ex, ey], i)
        item['points'].append([normal.nx(ex), normal.ny(ey)])
    elif e.dxftype() == 'ELLIPSE':
        x, y = e.dxf.center.x, e.dxf.center.y
        ux, uy = e.dxf.major_axis[0], e.dxf.major_axis[1]
        vx, vy = e.minor_axis[0], e.minor_axis[1]
        sp, ep = e.dxf.start_param, e.dxf.end_param
        points = ellipse(x, y, ux, uy, vx, vy, sp, ep)
        for p in points:
            if i is not None:
                p = _insert(p, i)
            item['points'].append([normal.nx(p[0]), normal.ny(p[1])])
    elif e.dxftype() == 'CIRCLE':
        x, y, r = e.dxf.center.x, e.dxf.center.y, e.dxf.radius
        points = arc(x, y, r)
        for p in points:
            if i is not None:
                p = _insert(p, i)
            item['points'].append([normal.nx(p[0]), normal.ny(p[1])])
    elif e.dxftype() == 'ARC':
        x, y, r = e.dxf.center.x, e.dxf.center.y, e.dxf.radius
        sa, ea = e.dxf.start_angle, e.dxf.end_angle
        points = arc(x, y, r, sa, ea)
        for p in points:
            if i is not None:
                p = _insert(p, i)
            item['points'].append([normal.nx(p[0]), normal.ny(p[1])])

    if item['type'] == 'text':
        tiny['texts'].append(item)
    else:
        tiny['lines'].append(item)


def doc2json(doc, meta):
    msp, normal = doc.modelspace(), Normal()
    tiny, inserts = {'meta': {}, 'lines': [], 'texts': []}, {}

    tiny['meta'] = {
        'name': meta['name'] if meta['name'] else 'default',
        'meta_id': meta['meta_id'] if meta['meta_id'] else 'default',
        'mine_code': meta['mine_code'] if meta['mine_code'] else 'default',
        'mine_name': meta['mine_name'] if meta['mine_name'] else 'default',
        'sign': meta['sign'] if meta['sign'] else 'default',
        'type': meta['type'] if meta['type'] else 'default',
        'piece': meta['piece'] if meta['piece'] else 'default',
        'major_ver': meta['major_ver'] if meta['major_ver'] else 'default',
        'minor_ver': meta['minor_ver'] if meta['minor_ver'] else 'default',
        'cs': meta['cs'] if meta['cs'] else 'default',
    }

    # 这里着重处理CAD的视图中心点
    vp = doc.viewports.entries['*active'][0].dxf
    vtx, vty, vcx, vcy = vp.target.x, vp.target.y, vp.center.x, vp.center.y
    if math.fabs(vtx) < THRESHOLD and math.fabs(vty) < THRESHOLD:
        tiny['meta']['x'], tiny['meta']['y'],  = vcx, vcy
        if vp.view_twist == 0:
            tiny['meta']['x'], tiny['meta']['y'] = vcx, vcy
        else:
            tiny['meta']['x'], tiny['meta']['y'] = twist(vcx, vcy, vp.view_twist)
    else:
        tiny['meta']['x'], tiny['meta']['y'] = vtx, vty

    # 初始化正则化数据
    normal.init(tiny['meta']['x'], tiny['meta']['y'])

    # 先处理insert实体
    for entity in msp:
        if entity.dxftype() == 'INSERT':
            insert = {
                'x': entity.dxf.insert.x,
                'y': entity.dxf.insert.y,
                'xs': entity.dxf.xscale,
                'ys': entity.dxf.yscale,
                'r': entity.dxf.rotation
            }

            blocks = doc.blocks.get(entity.dxf.name)
            if blocks is not None:
                insert['bx'], insert['by'] = blocks.base_point[0], blocks.base_point[1]
                for block in blocks:
                    _entity(block, tiny, tiny['meta']['meta_id'], normal, insert)

    # 再处理其它实体
    for entity in msp:
        if entity.dxftype() != 'INSERT':
            _entity(entity, tiny, tiny['meta']['meta_id'], normal)

    return json.dumps(tiny)


def dxf2json(file, meta):
    doc = ezdxf.readfile(filename=file, encoding='utf-8')
    return doc2json(doc, meta)
